//
//  MMGSocketClient.m
//  MyApp
//
//  Created by Don on 2017/8/28.
//  Copyright © 2017年 com.memego All rights reserved.
//

#import "MMGSocketClient.h"
#import <HWWeakTimer.h>
#import <CocoaAsyncSocket/GCDAsyncSocket.h>
#import "MMGSocketClient+MessageSender.h"
#import "NSDictionary+JsonString2Dic.h"
#import "MMGSocketMsgModel.h"
#import "NSDate+MMGTimestamp.h"

static int const Heart_Beat_Interval = 2;
static int const Heart_Beat_TimeOut_Interval = 18;
static float kSocket_Reconnect_Send_Sec = 2.0;//每隔2秒发送
//static int const kSocket_MaxReconnection_time = 5;

@interface MMGSocketClient ()<GCDAsyncSocketDelegate>
@property (nonatomic, strong) NSString *host;
@property (nonatomic, assign) uint16_t port;

@property (nonatomic, assign) MMGConstSocketConnectStatus socketConncetStatus;
@property (nonatomic, weak) NSTimer *heartbeatTimer;
@property (nonatomic, weak) NSTimer *sendHeartbeatTimeOutTimer;
@property (nonatomic, strong) GCDAsyncSocket *clientSocket;

@property (nonatomic, copy) NSMutableData *rvcBuffer;//socket 接受数据缓存
//@property (nonatomic, assign) BOOL need2ExitSendingThread;

@property (nonatomic, assign) int reconnection_time;
@property (nonatomic, weak) NSTimer *reconnecttionTimer;

//@property (nonatomic, assign)long long lastSendingDataTimestamp;//最后一个已发包的发包时间
//@property (nonatomic, assign) BOOL isManualCloseSocket;
@property (nonatomic, assign)NSTimeInterval lastReconnectTime;

@end
@implementation MMGSocketClient {
    dispatch_queue_t socketQueue;
    //BOOL isManualCloseSocket;
}

/*
+ (instancetype)shareMMGSocketClient {
    static MMGSocketClient *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[MMGSocketClient alloc] init];
    });
    return instance;
}
 */

- (instancetype)init {
    if ((self = [super init])) {
        socketQueue = dispatch_queue_create("com.memego.socket.queue", DISPATCH_QUEUE_SERIAL);
        self.socketConncetStatus = MMGConstSocketConnectStatusDisconnect;
        //isManualCloseSocket = NO;
    }
    return self;
}

- (void)dealloc {
   
    if (self && self.socketConncetStatus != MMGConstSocketConnectStatusDisconnect) {
        DLog(@"%@ dealloc", NSStringFromClass([self class]));
        [self closeSocket];
    }
}

- (void)connectServerHost:(NSString *)host port:(uint16_t)port {
    NSAssert(host.length != 0, @"Invalid host: Cannot connect : until host is not nil. ");
    self.host = host;
    self.port = port;
    self.socketConncetStatus = MMGConstSocketConnectStatusDisconnect;
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        do {
            if (self.socketConncetStatus == MMGConstSocketConnectStatusManualClose) {
                break;
            }
            
            if ((_clientSocket && self.socketConncetStatus == MMGConstSocketConnectStatusDisconnect) || !_clientSocket) {
                if (_clientSocket) {
                    sleep(3);
                }
                
                DLog(@"socke try reconnect ....");
                @synchronized(self)
                {
                    self.socketConncetStatus = MMGConstSocketConnectStatusConnecting;
                    [self reconnection];
                }
            }else {
                sleep(1);
                DLog(@"socke connected ....");
            }
            
        } while (1);
    });
    
    
    
}

- (void)reconnection {
    DLog(@"reconnection host:%@ , port:%hu",self.host,self.port);
    
    NSError *error = nil;
    //清空数据接收缓存
    //[self.rvcBuffer resetBytesInRange:NSMakeRange(0, [self.rvcBuffer length])];
    [self.rvcBuffer setLength:0];
    
    [self.clientSocket connectToHost:self.host onPort:self.port error:&error];
    if (error) {
        DLog(@"host:%@ port:%u  connect error :%@",self.host,self.port,error.localizedDescription);
        [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidFailWithError object:error];
    }
    //[self connectServerHost:self.host port:self.port];
    
}
- (void)closeSocket {
    
    @synchronized(self)
    {
        _socketConncetStatus = MMGConstSocketConnectStatusManualClose;
        [self stopHeartbeatTimer];
        
        //清空数据接收缓存
        [_rvcBuffer setLength:0];
        _rvcBuffer = nil;
        
        if (_clientSocket) {
            [_clientSocket disconnect];
            [_clientSocket setDelegate:nil];
            _clientSocket = nil;
        }
        [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidClose object:nil];
    }
    //KILL SENDING THREAD
//    self.need2ExitSendingThread = YES;
    
//    [self stopHeartbeatTimeOutTimer];
    
    
    
//    [self.sendingDataBuffer removeAllObjects];
}

- (void)sendMessage:(NSData *)msg {
    if (msg == nil) {
        DLog(@"message is null ");
        return;
    }
    dispatch_async(socketQueue, ^{
        
        if (self.socketConncetStatus == MMGConstSocketConnectStatusConnected) {
            [self.clientSocket writeData:msg withTimeout:-1 tag:0];
        }
        
    });
}
#pragma mark - socket delegate
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
    //    [sock readDataWithTimeout:-1 tag:0];
    DLog(@"didConnectToHost ....");
    self.socketConncetStatus = MMGConstSocketConnectStatusConnected;
    // 连接后,可读取服务端的数据
    [sock readDataToData:[GCDAsyncSocket ZeroData] withTimeout:-1 tag:0];
    [self sendMessage:[self heartBeatMsg]];
    //isManualCloseSocket = NO;
    //        [self startHeartbeatTimer];
    //    self.need2ExitSendingThread = NO;
    //    dispatch_async(dispatch_get_global_queue(0, DISPATCH_QUEUE_SERIAL), ^{
    //        [self sendingDataThread];
    //    });
    
    [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidOpen object:nil];
}

- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    [sock readDataToData:[GCDAsyncSocket ZeroData] withTimeout:200 tag:tag];
}

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    @synchronized(self)
    {
    @try {
        DLog(@"---------- didReadData length %lu tap %d-------",(unsigned long)data.length,(int)tag);
        if (data.length > 0 ) {
            
            [self.rvcBuffer appendBytes:[data bytes] length:data.length];
            DLog(@"did recivee .. buffer length %lu",(unsigned long)self.rvcBuffer.length);
            if (self.rvcBuffer.length >= 4) {
                
                uint8_t* p = (uint8_t*)[self.rvcBuffer bytes];
                int len;
                len = (((int)p[0]) << 24) | (((int)p[1]) << 16) | (((int)p[2]) << 8) | (int)p[3];
                DLog(@"curretn len :%d",len);
                DLog(@"---- self.rvcBuffer.length %d",self.rvcBuffer.length);
                if ((len +4) <= self.rvcBuffer.length) {
                    NSRange jsonDataRange = (NSRange){4,len};
                    NSData *_data = [self.rvcBuffer subdataWithRange:jsonDataRange];
                    NSString *recieveStr = [[NSString alloc] initWithBytes:[_data bytes] length:_data.length encoding:NSUTF8StringEncoding];
                    
                    unsigned long ramainLength = self.rvcBuffer.length - (len +4);
                    if (ramainLength > 0) {
                        DLog(@"!!!!!!!!!!!!!!!!!!!!!!!-----------------------------------------------------");
                        
                        NSData *remainData = [self.rvcBuffer subdataWithRange:(NSRange){(len+4),ramainLength}];
                        [self.rvcBuffer setLength:0];
                        DLog(@"---------------------------self.rvcBuffer.length %d--------------------------",self.rvcBuffer.length);
                        [self.rvcBuffer setData:remainData];
                    }else {
                        DLog(@"！！！！！！！！！！！！！！！！！！！---------------------------self.rvcBuffer.length %d--------------------------",self.rvcBuffer.length);
                        [self.rvcBuffer setLength:0];
                        
                    }
                    
                    DLog(@"--------------------did recieve string %@", recieveStr);
                    NSRange range = [recieveStr rangeOfString:@"{"];
                    if (range.location != NSNotFound) {
                        NSString *jsonStr = [recieveStr substringFromIndex:range.location];
                        NSDictionary *jsDic = [NSDictionary mmg_dictionaryWithJsonString:jsonStr];
                        DLog(@"---did recieve dictionary %@",jsDic);
                        MMGSocketMsgModel *msg = [MMGSocketMsgModel modelFromDataJson:jsDic];
                        if (msg.type == EMMMGSocketMsgAckTypeSyncTime) {
                            [self updateServerTime:msg.timestamp];
                            [self startHeartbeatTimer];
                        }
                        
                        //心跳续命，只要能收到消息都代表着客户端还活着，心跳超时监听必须在未来 Heart_Beat_TimeOut_Interval 后再触发
                        //        [self heartbeatTimerTimeOutContinue];
                        DLog(@"~~~~~~~~post notification~~~~~~~~~");
                        [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidReceiveMsg object:msg];
                    }
                    
                }
            }
            // 连接后,可读取服务端的数据
            [sock readDataToData:[GCDAsyncSocket ZeroData] withTimeout:-1 tag:tag+1];
            
        }
    } @catch (NSException *exception) {
        DLog(@"catch exception %@",exception.description);
        [self.rvcBuffer setLength:0];
    } @finally {
        
    }
    
    }
}
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    
    @synchronized(self)
    {
         DLog(@"???????????????????????????????  socket did disconnect .....");
        if (self.socketConncetStatus != MMGConstSocketConnectStatusManualClose) {
             DLog(@"not manual close MMGConstSocketConnectStatusDisconnect  socket did disconnect .....");
            self.socketConncetStatus = MMGConstSocketConnectStatusDisconnect;
            //清空数据接收缓存
            [self.rvcBuffer setLength:0];
        }
    
    }
    /*
        if (self.isManualCloseSocket || !err) {//手动关闭socket时 err = nil
            [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidClose object:nil];
            return;
        }

        if ([self checkReconnection]) {
            return;
        }
        self.lastReconnectTime = [NSDate date].timeIntervalSince1970;
        DLog(@"!!!!!!!!!!  socket reconnect .....");
        [self reconnection];
         */
    //    if(_reconnection_time>=0 && _reconnection_time <= kSocket_MaxReconnection_time) {
    //        
    //        [_reconnecttionTimer invalidate];
    //        _reconnecttionTimer=nil;
    //        [self reconnection];
    ////        int time =pow(1,_reconnection_time);
    ////        _reconnecttionTimer= [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(reconnection) userInfo:nil repeats:NO];
    //        
    //        _reconnection_time++;
    //        DLog(@"socket did reconnection,after %d try again",_reconnection_time);
    //    }else{
    //        
    //        _reconnection_time=0;
    //        DLog(@"socket Did Disconnect:%p withError: %@", sock, err);
    //        [self closeSocket];
    //        [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidClose object:nil];
    //    }

    //    [self closeSocket];
    //    [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidClose object:nil];
}

#pragma mark - private m
- (void)stopHeartbeatTimeOutTimer {
    if (_sendHeartbeatTimeOutTimer) {
        [_sendHeartbeatTimeOutTimer invalidate];
        _sendHeartbeatTimeOutTimer = nil;
    }
}
- (void)stopHeartbeatTimer {
    if (_heartbeatTimer) {
        [_heartbeatTimer invalidate];
        _heartbeatTimer = nil;
    }
}
- (void)startHeartbeatTimer {
    [self stopHeartbeatTimer];
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.heartbeatTimer fire];
    });
    
    
}

- (void)heartbeatTimerTimeOutContinue {
    NSDate *date = [NSDate date];
    self.sendHeartbeatTimeOutTimer.fireDate = [NSDate dateWithTimeInterval:Heart_Beat_TimeOut_Interval sinceDate:date];
    DLog(@"心跳超时日期 %@ --------- 下一次心跳时间",self.sendHeartbeatTimeOutTimer.fireDate);
}

//TODO: - 校对服务器时间
- (void)updateServerTime:(long long)timestamp {
    NSTimeInterval timeInteval = timestamp - [NSDate mmg_currentTimeStamp];
    self.syncTimestampDifference = timeInteval;
}

//TODO: - 发送数据队列（包含心跳）
//- (void)sendingDataThread {
//    
//    do {
//        
//        long long currentTimestamp = [NSDate mmg_currentTimeStamp];
//        if (self.sendingDataBuffer.count > 0) {
//            NSLock *lock = [[NSLock alloc] init];
//            [lock lock];
//            
//            NSData *sendingData = self.sendingDataBuffer.firstObject;
//            if (sendingData != nil) {
//                [self sendMessage:sendingData];
//                if ([self.sendingDataBuffer containsObject:sendingData]) {
//                     [self.sendingDataBuffer removeObject:sendingData];
//                }
//               
//                self.lastSendingDataTimestamp = currentTimestamp;
//            }
//            
//            
//            [lock unlock];
//        }
//        else {
//            
//            if (currentTimestamp - self.lastSendingDataTimestamp > Heart_Beat_Interval*1000) {
//                [self.clientSocket writeData:[self heartBeatMsg] withTimeout:-1 tag:0];
//                self.lastSendingDataTimestamp = currentTimestamp;
//            }
////            sleep(Heart_Beat_Interval);
////            [self.clientSocket writeData:[self heartBeatMsg] withTimeout:-1 tag:0];
//        }
//        
//        
//        
//    } while (!self.need2ExitSendingThread);
//
//}

#pragma mark - lazy init
- (GCDAsyncSocket *)clientSocket {
    if (!_clientSocket) {
        _clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:socketQueue];
    }
    
    return _clientSocket;
}

- (NSTimer *)heartbeatTimer {
    if (!_heartbeatTimer) {
        MMGWeakSelf(weakSelf);
        _heartbeatTimer = [HWWeakTimer scheduledTimerWithTimeInterval:Heart_Beat_Interval block:^(id userInfo) {
            if (weakSelf.socketConncetStatus == MMGConstSocketConnectStatusConnected) {
                //handle heart
                [weakSelf.clientSocket writeData:[self heartBeatMsg] withTimeout:-1 tag:0];
            }
            
        } userInfo:nil repeats:YES];
        //此处把timer 添加到当前RUNLOOP 的NSRunLoopCommonModes模式，以防止scrollview 滚动影响
        [[NSRunLoop currentRunLoop] addTimer:_heartbeatTimer forMode:NSRunLoopCommonModes];
    }
    
    return _heartbeatTimer;
}
- (NSTimer *)sendHeartbeatTimeOutTimer {
    if (!_sendHeartbeatTimeOutTimer) {
        _sendHeartbeatTimeOutTimer = [HWWeakTimer scheduledTimerWithTimeInterval:Heart_Beat_TimeOut_Interval block:^(id userInfo) {
            //handle heartbeat time out ..
            DLog(@"心跳超时 --------------------      ");
            [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketHeartOutTime object:nil];
            [self closeSocket];
            
        } userInfo:nil repeats:NO];
        //此处把timer 添加到当前RUNLOOP 的NSRunLoopCommonModes模式，以防止scrollview 滚动影响
        [[NSRunLoop currentRunLoop] addTimer:_sendHeartbeatTimeOutTimer forMode:NSRunLoopCommonModes];
    }
    
    return _sendHeartbeatTimeOutTimer;
}

- (NSMutableData *)rvcBuffer {
    if (!_rvcBuffer) {
        _rvcBuffer = [[NSMutableData alloc] init];
    }
    
    return _rvcBuffer;
}
- (NSMutableArray *)sendingDataBuffer {
    if (!_sendingDataBuffer) {
        _sendingDataBuffer = [[NSMutableArray alloc] init];
    }
    return _sendingDataBuffer;
}

- (int)checkReconnection {
    NSTimeInterval timeDiff = [NSDate date].timeIntervalSince1970 - self.lastReconnectTime;
    if(timeDiff >= kSocket_Reconnect_Send_Sec || self.lastReconnectTime == 0){
        return 0;
    }else{
        return kSocket_Reconnect_Send_Sec - timeDiff;
    }
}


@end
